package cn.juque.lucenecandy.helper;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.juque.common.exception.AppException;
import cn.juque.common.utils.FastJsonUtil;
import cn.juque.lucenecandy.bo.FieldBO;
import cn.juque.lucenecandy.bo.IndexBO;
import cn.juque.lucenecandy.cache.IndexInfoCache;
import cn.juque.lucenecandy.core.base.BaseEntity;
import cn.juque.lucenecandy.core.constants.StrConstant;
import cn.juque.lucenecandy.core.enums.DataTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.document.*;
import org.apache.lucene.util.BytesRef;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author juque
 * @version 1.0.0
 * <ul>
 *     <li>DocumentHelper</li>
 * </ul>
 * @date 2023-12-29
 **/
@Slf4j
@Component("documentHelper")
public class DocumentHelper {

    @Resource
    private IndexInfoCache indexInfoCache;

    /**
     * 获取索引信息
     *
     * @param clazz 类
     * @return IndexBO
     */
    public IndexBO index(Class<? extends BaseEntity> clazz) {
        return this.indexInfoCache.get(ClassUtil.getClassName(clazz, false), false);
    }

    /**
     * 获取主键值
     *
     * @param entity 实体类
     * @param <T>    T
     * @return String
     */
    public <T extends BaseEntity> String id(T entity) {
        Object value = ReflectUtil.getFieldValue(entity, StrConstant.ID);
        return Objects.nonNull(value) ? value.toString() : CharSequenceUtil.EMPTY;
    }

    public <T extends BaseEntity> List<Document> toDocumentList(List<T> list) {
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        return list.stream().map(this::toDocument).collect(Collectors.toList());
    }

    /**
     * 实体类转文档
     *
     * @param entity 实体类
     * @param <T>    T
     * @return Document
     */
    public <T extends BaseEntity> Document toDocument(T entity) {
        IndexBO indexBO = this.indexInfoCache.get(ClassUtil.getClassName(entity, false), false);
        Map<String, FieldBO> fieldMap = indexBO.getFieldMap();
        Document document = new Document();
        for (Map.Entry<String, FieldBO> entry : fieldMap.entrySet()) {
            String fieldName = entry.getKey();
            FieldBO bo = entry.getValue();
            Object value = ReflectUtil.getFieldValue(entity, fieldName);
            if (Objects.isNull(value)) {
                continue;
            }
            switch (bo.getDataType()) {
                case STRING:
                    this.toDocumentStr(document, bo, value);
                    break;
                case DATE:
                    this.toDocumentDate(document, bo, value);
                    break;
                case LONG:
                    this.toDocumentLong(document, bo, value);
                    break;
                case INT:
                    this.toDocumentInt(document, bo, value);
                    break;
                case DECIMAL:
                    this.toDocumentDecimal(document, bo, value);
                    break;
                default:
                    break;
            }
        }
        return document;
    }

    /**
     * 文档转实体类
     *
     * @param docList 文档
     * @param tClass  类
     * @param <T>     T
     * @return List
     */
    public <T extends BaseEntity> List<T> toEntityList(List<Document> docList, Class<T> tClass) {
        if (CollUtil.isEmpty(docList)) {
            return new ArrayList<>(0);
        }
        return docList.stream().map(f -> this.toEntity(f, tClass)).collect(Collectors.toList());
    }

    /**
     * json转实体类
     *
     * @param json      json字符串
     * @param className 类名
     * @return List
     */
    public <T extends BaseEntity> List<T> toEntityList(String json, String className) {
        Class<T> tClass = ClassUtil.loadClass(className);
        return this.toEntityList(json, tClass);
    }

    /**
     * json转实体类
     *
     * @param json   json字符串
     * @param tClass 类
     * @param <T>    T
     * @return List
     */
    public <T extends BaseEntity> List<T> toEntityList(String json, Class<T> tClass) {
        if (CharSequenceUtil.isEmpty(json)) {
            return new ArrayList<>(0);
        }
        return FastJsonUtil.parseArray(json, tClass);
    }

    /**
     * 文档转实体类
     *
     * @param document 文档
     * @param tClass   类
     * @param <T>      T
     * @return T
     */
    public <T extends BaseEntity> T toEntity(Document document, Class<T> tClass) {
        T entity;
        try {
            entity = ReflectUtil.newInstance(tClass);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new AppException("【" + tClass.getName() + "】instance error");
        }
        IndexBO indexBO = this.indexInfoCache.get(ClassUtil.getClassName(entity, false), false);
        Map<String, FieldBO> fieldMap = indexBO.getFieldMap();
        for (Map.Entry<String, FieldBO> entry : fieldMap.entrySet()) {
            String fileName = entry.getKey();
            FieldBO bo = entry.getValue();
            String indexFileName = bo.getFieldName();
            String value = document.get(indexFileName);
            if (CharSequenceUtil.isEmpty(value)) {
                continue;
            }
            DataTypeEnum dataType = bo.getDataType();
            switch (dataType) {
                case STRING:
                    ReflectUtil.setFieldValue(entity, fileName, value);
                    break;
                case INT:
                    ReflectUtil.setFieldValue(entity, fileName, Integer.parseInt(value));
                    break;
                case LONG:
                    ReflectUtil.setFieldValue(entity, fileName, Long.parseLong(value));
                    break;
                case DATE:
                    ReflectUtil.setFieldValue(entity, fileName, new Date(Long.parseLong(value)));
                    break;
                case DECIMAL:
                    ReflectUtil.setFieldValue(entity, fileName, new BigDecimal(value));
                    break;
                default:
                    break;
            }
        }
        return entity;
    }

    /**
     * 往document添加字符串内容
     *
     * @param bo    字段信息
     * @param value 值
     */
    private void toDocumentStr(Document document, FieldBO bo, Object value) {
        if (Boolean.TRUE.equals(bo.getIndexed())) {
            document.add(new TextField(bo.getFieldName(), String.valueOf(value), bo.getStore()));
        } else {
            document.add(new StringField(bo.getFieldName(), String.valueOf(value), bo.getStore()));
        }
        if (Boolean.TRUE.equals(bo.getGroupBy())) {
            document.add(new SortedDocValuesField(bo.getFieldName(), new BytesRef(String.valueOf(value))));
        }
    }

    /**
     * 往document添加日期内容
     *
     * @param bo    字段信息
     * @param value 值
     */
    private void toDocumentDate(Document document, FieldBO bo, Object value) {
        document.add(new NumericDocValuesField(bo.getFieldName(), ((Date) value).getTime()));
        if (org.apache.lucene.document.Field.Store.YES.equals(bo.getStore())) {
            document.add(new StoredField(bo.getFieldName(), ((Date) value).getTime()));
        }
        document.add(new LongPoint(bo.getFieldName(), ((Date) value).getTime()));
        if (Boolean.TRUE.equals(bo.getGroupBy())) {
            long l = ((Date) value).getTime();
            document.add(new SortedDocValuesField(bo.getFieldName(), new BytesRef(Long.toString(l))));
        }
    }

    /**
     * 往document添加长整型内容
     *
     * @param bo    字段信息
     * @param value 值
     */
    private void toDocumentLong(Document document, FieldBO bo, Object value) {
        document.add(new NumericDocValuesField(bo.getFieldName(), Long.parseLong(value.toString())));
        if (org.apache.lucene.document.Field.Store.YES.equals(bo.getStore())) {
            document.add(new StoredField(bo.getFieldName(), Long.parseLong(value.toString())));
        }
        document.add(new LongPoint(bo.getFieldName(), Long.parseLong(value.toString())));
    }

    /**
     * 往document添加整型内容
     *
     * @param bo    字段信息
     * @param value 值
     */
    private void toDocumentInt(Document document, FieldBO bo, Object value) {
        if (org.apache.lucene.document.Field.Store.YES.equals(bo.getStore())) {
            document.add(new StringField(bo.getFieldName(), String.valueOf(value), bo.getStore()));
        }
        document.add(new IntPoint(bo.getFieldName(), Integer.parseInt(value.toString())));
        if (Boolean.TRUE.equals(bo.getGroupBy())) {
            document.add(new SortedDocValuesField(bo.getFieldName(), new BytesRef(Integer.parseInt(value.toString()))));
        }
    }

    /**
     * 往document添加Decimal内容
     *
     * @param bo    字段信息
     * @param value 值
     */
    private void toDocumentDecimal(Document document, FieldBO bo, Object value) {
        document.add(new StringField(bo.getFieldName(), String.valueOf(value), bo.getStore()));
        if (Boolean.TRUE.equals(bo.getIndexed())) {
            document.add(new DoublePoint(bo.getFieldName(), Double.parseDouble(value.toString())));
        }
        if (Boolean.TRUE.equals(bo.getGroupBy())) {
            document.add(new SortedDocValuesField(bo.getFieldName(), new BytesRef(String.valueOf(value))));
        }
    }
}
